﻿var Constants = {
    Mode: {
        REVIEW: 0,
        UPDATE: 1,
        VIEW: 2
    }
};

$(document).ready(function () {
    var i = 1;
    $(document).on("focus", "[contenteditable]", function () {
        var $this = $(this);
        $this.data("before-blur", $this.html()).data('before-keyup', $this.html());
        return $this;
    }).on("blur", "[contenteditable]", function () {
        var $this = $(this);
        if ($this.data('before-blur') !== $this.html()) {
            $this.trigger('change');
        }
        return $this;
    }).on("keyup", "[contenteditable]", function () {
        var $this = $(this);
        if ($this.data('before-keyup') !== $this.html()) {
            $this.data('before-keyup', $this.html());
            $this.trigger('changing');
        }
        return $this;
    });

    ko.bindingHandlers.contenteditable = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            var value = ko.utils.unwrapObservable(valueAccessor());

            $(element).html(value).attr('contenteditable', 'true').change(function () {
                valueAccessor()($(this).html());
            })
        },
        update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            ko.bindingHandlers.html.update(element, valueAccessor);
        }
    };
    ko.bindingHandlers.code = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        },
        update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            ko.bindingHandlers.text.update(element, valueAccessor);
        }
    };
    ko.bindingHandlers.comment = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            var value = ko.utils.unwrapObservable(valueAccessor());

            var comment = $(element).parent().commentDialog(value, {
                direction: 'right',
                container: $('#code')
            });
            value.dialog = comment;
        },
        update: function (element, valueAccessor, allBindingsAccessor, viewModel) {
        }
    };

    $.fn.commentDialog = function (data, options) {
        return new CommentDialog(this, data, options).init();
    };
    function CommentDialog(element, data, options) {
        var self = this;

        this.element = element;
        this.data = data;
        this.options = options;

        this.focus = function (mode) {
            var problem = self.element.find('.problem');
            var response = self.element.find('.response');
            var isFocused = document.activeElement == problem[0] || document.activeElement == response[0];

            if (!isFocused) {
                switch (mode) {
                    case Constants.Mode.VIEW:
                        break;
                    case Constants.Mode.REVIEW:
                        problem[0].focus();
                        break;
                    case Constants.Mode.UPDATE:
                        response[0].focus();
                        break;
                }
            }
        };
        this.remove = function () {
            self.mark.replaceWith(self.mark.contents());
            self.element.remove();
            return self;
        };
        this.init = function () {
            var container = options.container;
            var code = options.container.find('pre');
            code.reindex();

            var range = code.getRange(self.data.offset(), self.data.length());
            self.mark = $('comment', self.element);
            range.surroundContents(self.mark[0]);

            $(self.element).resize(function () {
                self.updatePos();
            }).trigger('resize').trigger('resize');

            return self;
        };
        this.updatePos = function () {
            self.element.rdialog({
                target: self.mark,
                dock: self.options.direction,
                closeOnEsc: false
            });

            return self;
        };
    }

    $.fn.reindex = function (index) {
        if (!index) {
            index = 0;
        }
        this.contents().each(function () {
            $(this).data({
                offset: index,
                length: $(this).text().length
            });
            if (this.nodeType != Node.TEXT_NODE) {
                $(this).reindex(index);
            }
            index += $(this).text().length;
        });
    };
    $.fn.getLocation = function (offset) {
        var contents = $(this).contents();
        for (var i = 0; i < contents.length; i++) {
            var o = $(contents[i]).data('offset');
            var l = $(contents[i]).data('length');

            if (o <= offset && o + l >= offset) {
                if (contents[i].nodeType == Node.TEXT_NODE) {
                    return {
                        node: contents[i],
                        offset: offset - o
                    };
                }
                else {
                    return $(contents[i]).getLocation(offset);
                }
            }
        }
    };
    $.fn.getRange = function (offset, length) {
        var start = $(this).getLocation(offset);
        var end = $(this).getLocation(offset + length);

        var range = document.createRange();
        range.setStart(start.node, start.offset);
        range.setEnd(end.node, end.offset);
        return range;
    };

    function Comment() {
        var self = this;

        this.review = null;
        this.dialog = null;

        this.offset = ko.observable();
        this.length = ko.observable();
        this.location = ko.observable();
        this.code = ko.observable();

        this.problem = ko.observable();
        this.response = ko.observable();

        this.datetime = ko.observable();

        this.init = function () {
            self.offset(0);
            self.length(0);
            self.code('');
            self.problem('');
            self.response('');
            self.datetime('');
        };
        this.remove = function () {
            self.dialog.remove();
            self.review.comments.remove(self);
        };
        this.select = function () {
            if (self.review.selectedComment() !== self) {
                self.review.selectedComment(self);
                self.dialog.focus(self.review.mode());
            }
        };
        this.isSelected = function () {
            return self == self.review.selectedComment();
        };
        this.import = function (data) {
            for (var key in data) {
                this[key](data[key]);
            }
        };
        this.export = function () {
            return {
                offset: self.offset(),
                length: self.length(),
                code: self.code(),
                problem: self.problem(),
                response: self.response(),
                datetime: self.datetime()
            };
        };
        this.dock = function (direction) {
            if (self.dialog.options.direction != direction) {
                self.dialog.options.direction = direction;
                self.dialog.updatePos();
            }
        };
        this.dockLeft = function () { self.dock('left'); };
        this.dockTop = function () { self.dock('top'); };
        this.dockRight = function () { self.dock('right'); };
        this.dockBottom = function () { self.dock('bottom'); };
    }

    function Review() {
        var self = this;

        this.mode = ko.observable(Constants.Mode.REVIEW);

        this.id = ko.observable();
        this.filename = ko.observable();

        this.codefile = ko.observable();
        this.creator = ko.observable();
        this.reviewer = ko.observable();
        this.reviewDate = ko.observable();
        this.comments = ko.observableArray();
        this.selectedComment = ko.observable();

        this.code = ko.observable();
        this.linecount = ko.observable();
        this.linenums = ko.observable();

        var locked = false;
        var i = 0;
        $(window).keydown(function (e) {
            if (!$(document.activeElement).hasAttr('contenteditable')) {
                switch (e.keyCode) {
                    case 32:
                        e.preventDefault();
                        self.addComment();
                        break;
                }
            }
        });

        this.getFilename = function () {
            return 'file';
        };

        this.select = function (comment) {
            this.selectedComment(comment);
        };
        this.init = function () {
            self.id('');
            self.filename('');

            self.codefile('');
            self.creator('');
            self.reviewer('');
            self.reviewDate('');
            self.comments([]);
            self.selectedComment(null);
            self.code('');
            self.linecount(0);
            self.linenums('');
        };

        this.import = function (data) {
            self.init();
            if (typeof data.id !== 'undefined') {
                self.id(data.id);
            }
            if (typeof data.filename !== 'undefined') {
                self.filename(data.filename);
            }

            var review = data.review;
            if (typeof review == 'string') {
                review = JSON.parse(review);
            }
            for (var key in review) {
                switch (key) {
                    case 'comments':
                        var comments = [];
                        for (var i = 0; i < review[key].length; i++) {
                            var comment = new Comment();
                            comment.review = self;
                            comment.import(review[key][i]);
                            comments.push(comment);
                        }
                        self.comments(comments);
                        break;
                    case 'code':
                        self[key](review[key]);
                        self.linecount(self.code().split('\n').length);
                        var linenums = '';
                        for (var i = 1; i <= self.linecount(); i++) {
                            linenums += i + '\n';
                        }
                        linenums += '\n\n\n';
                        self.linenums(linenums);
                        break;
                    default:
                        self[key](review[key]);
                        break;
                }
            }
            $('.posizable').posize(true);
        };
        this.export = function () {
            var data = {
                codefile: self.codefile(),
                creator: self.creator(),
                reviewer: self.reviewer(),
                reviewDate: self.reviewDate(),
                code: self.code(),
                comments: []
            };
            var comments = self.comments();
            for (var i = 0; i < comments.length; i++) {
                try {
                    data.comments.push(comments[i].export());
                }
                catch (err) {
                    window.external.Alert(err);
                }
            }
            return data;
        };
        this.addComment = function (item, e) {
            $('#code pre').reindex();

            var selection = document.getSelection();
            var range = selection.getRangeAt(0);
            var root = $('#code');
            if (range.toString().length <= 0 || !$(range.startContainer).hasParent(root) || !$(range.endContainer).hasParent(root) ||
                $(range.startContainer).hasParent("comment") || $(range.endContainer).hasParent("comment")) {
                return;
            }

            var comment = new Comment();
            comment.review = self;
            comment.offset($(range.startContainer).data('offset') + range.startOffset);
            comment.length(range.toString().length);
            comment.code(range.toString());
            comment.problem('&nbsp;');
            comment.datetime(new Date().format('yyyy/MM/dd HH:mm:ss'));

            self.comments.push(comment);
            comment.select();
        };
    }

    var services = {
        local: {
            open: function () {
                Services.local({
                    method: 'open',
                    params: review.filename(),
                    complete: function (data) {
                        if (data) {
                            review.import(data);
                        }
                    }
                });
            },
            save: function (success, error) {
                Services.local({
                    method: 'save',
                    params: { filename: review.filename(), review: JSON.stringify(review.export()) },
                    complete: function (data) {
                        if (data) {
                            review.filename(data);
                        }
                        if (success) {
                            success(data);
                        }
                    }
                });
            },
            saveAs: function () {
                Services.local({
                    method: 'save-as',
                    params: { filename: review.filename(), review: JSON.stringify(review.export()) },
                    complete: function (data) {
                        if (data) {
                            review.filename(data);
                        }
                    }
                });
            },
            close: function () {
                Services.local({ method: 'close' });
            },
            openFolder: function (path) {
                Services.local({ method: 'open-folder', params: path });
            }
        },
        remote: {
            remoteUrl: 'http://localhost:8732/RSuite/R2/InvokeMethod',
            open: function (id, success, error) {
                Services.remote('open', id, {
                    url: this.remoteUrl,
                    type: 'POST',
                    success: function (data) {
                        if (data) {
                            review.import(data);
                        }
                        if (success) {
                            success(data);
                        }
                    },
                    error: function (data) {
                        if (error) {
                            error(data);
                        }
                    }
                });
            },
            save: function (success, error) {
                Services.remote('save', { id: review.id(), review: JSON.stringify(review.export()) },
                {
                    url: this.remoteUrl,
                    type: 'POST',
                    success: function (data) {
                        if (data) {
                            review.id(data);
                        }
                        if (success) {
                            success(data);
                        }
                    },
                    error: function () {
                        alert(2);
                        if (error) {
                            error(data);
                        }
                    }
                });
            }
        }
    };

    var review = new Review();
    var dialogs = {
        local: {
            save: {
                filename: ko.observable(''),
                show: function () {
                    $('#local-save-dialog').rdialog({
                        target: menu.file.item.children(),
                        dock: 'bottom left',
                        timeout: 3000
                    });
                },
                openFolder: function () {
                    services.local.openFolder(this.filename());
                }
            }
        },
        remote: {
            save: {
                id: ko.observable(),
                show: function () {
                    $('#remote-save-dialog').rdialog({
                        target: menu.file.item.children(),
                        dock: 'bottom left',
                        timeout: 3000
                    });
                }
            },
            open: {
                NORMAL: 1,
                LOADING: 2,
                ERROR: 3,
                id: ko.observable(''),
                status: ko.observable(1),
                message: ko.observable(''),

                openReview: function () {
                    var self = this;
                    self.status(self.LOADING);

                    services.remote.open(this.id(),
                        function (data) {
                            self.hide();
                        },
                        function (data) {
                            self.status(self.ERROR);
                            self.message(data.message);
                        });
                },
                show: function () {
                    var self = this;
                    self.status(self.NORMAL);
                    self.message('');

                    $('#remote-open-dialog').rdialog({
                        target: menu.file.item.children(),
                        dock: 'bottom left'
                    });
                },
                hide: function () {
                    $('#remote-open-dialog').hide();
                }
            }
        },
        about: {
            show: function () {
                $('#about-dialog').rdialog({
                    target: menu.about.item.children(),
                    dock: 'bottom left'
                });
            },
            hide: function () {
                $('#about-dialog').hide();
            }
        }
    };
    var self = this;
    var menu = {
        file: {
            caption: 'File',
            items: {
                open: {
                    caption: 'Open...',
                    click: function () {
                        services.local.open();
                    },
                    mode: 'client'
                },
                save: {
                    caption: 'Save',
                    click: function () {
                        services.local.save(
                            function (data) {
                                if (data) {
                                    dialogs.local.save.filename(data);
                                    dialogs.local.save.show();
                                }
                            });
                    },
                    mode: 'client'
                },
                saveAs: {
                    caption: 'Save as...',
                    click: function () {
                        services.local.saveAs();
                    },
                    mode: 'client'
                },
                h0: {
                    caption: '-',
                    mode: 'client'
                },
                openServer: {
                    caption: 'Open from server...',
                    click: function () {
                        dialogs.remote.open.show();
                    },
                    mode: 'client server'
                },
                saveServer: {
                    caption: 'Save to server...',
                    click: function () {
                        services.remote.save(
                            function (data) {
                                dialogs.remote.save.id(data);
                                dialogs.remote.save.show();
                            },
                            function (data) {
                            });
                    },
                    mode: 'client server'
                },
                h1: {
                    caption: '-',
                    mode: 'client'
                },
                close: {
                    caption: 'Close',
                    click: function () {
                        services.local.close();
                    },
                    mode: 'client'
                }
            }
        },
        exportMenu: {
            caption: 'Export',
            items: {
                toHtml: {
                    caption: 'To HTML...',
                    click: function (e) {
                    }
                },
                toExcel: {
                    caption: 'To Excel...',
                    click: function (e) {
                    }
                }
            }
        },
        personal: {
            caption: 'Personal',
            items: {
                records: {
                    caption: 'Records...',
                    click: function (e) {
                    }
                }
            }
        },
        about: {
            caption: 'About',
            click: function () {
                dialogs.about.show();
            }
        }
    };

    //    for (var i = 0; i < 20; i++) {
    //        var comment = new Comment();
    //        comment.review = review;
    //        comment.offset(Math.floor(Math.random() * (review.code().length - 20)));
    //        comment.length(Math.floor(Math.random() * 10 + 1));
    //        comment.code(review.code().substring(comment.offset(), comment.offset() + comment.length()));
    //        comment.location(i);
    //        comment.problem('Nên sửa thành...');
    //        comment.response('');
    //        comment.datetime(new Date().format('yyyy/MM/dd HH:mm:ss'));

    //        review.comments.push(comment);
    //    }

    $(window).resize(function () {
        $('.vscroll').each(function () {
            $(this).width($(this).parent().width());
        });
        $('#code').trigger('scroll');
    });
    $('#code').scroll(function () {
        $('#linenums').scrollTop($(this).scrollTop());
    });

    $('#menu').rmenu(menu);
    $('[knockout]').each(function () {
        ko.applyBindings(eval($(this).attr('knockout')), this);
    });
    $('.posizable').posize();

    review.import({
        filename: 'c:\\temp\\test.rvw',
        review: {
            codefile: 'c:\\temp\\test.cs',
            creator: 'AnhTT',
            reviewer: 'TuanMA',
            reviewDate: '2012/06/14',
            code: 'imports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;imports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\nimports System;\r\n...'
        }
    });
});

$(window).load(function () {
    $(window).trigger('resize');
});